package org.marverse.serialization

import java.nio.ByteBuffer
import java.nio.channels.{ReadableByteChannel, WritableByteChannel}
import scala.collection.mutable.ArrayBuffer
import java.util.IdentityHashMap
import util.{MutableTypedMap, TypedKey}

class ObjectInputChannel(channel: ReadableByteChannel, registry: ClassRegistry)
extends ObjectInput {

  def readBytes(buffer: ByteBuffer) {
    channel.read(buffer)
  }

  private val buffer: ByteBuffer = ByteBuffer.allocate(8)
  @throws(classOf[java.io.IOException])
  private def readBytes(num: Int) {
    buffer.clear()
    buffer.limit(num)
    readBytes(buffer)
    buffer.flip()
  }
  @throws(classOf[java.io.IOException])
  def readBoolean = {
    readBytes(1)
    buffer.get() > 0
  }
  @throws(classOf[java.io.IOException])
  def readChar = { 
    readBytes(2)
    buffer.getChar()
  }
  @throws(classOf[java.io.IOException])
  def readInt = { 
    readBytes(4)
    buffer.getInt()
  }
  @throws(classOf[java.io.IOException])
  def readLong = { 
    readBytes(8)
    buffer.getLong()
  }
  @throws(classOf[java.io.IOException])
  def readDouble = { 
    readBytes(8)
    buffer.getDouble()
  }  
  
  val backref = new ArrayBuffer[AnyRef]()
  @throws(classOf[java.io.IOException])
  def read[T <: AnyRef](clazz: Class[T])(implicit ctx: MutableTypedMap[TypedKey, Any]): T = {
    var id: ClassRegistry#ID = readInt
    if (id == 0)
      clazz.cast(backref(readInt))
    else {
      val result = clazz.cast(registry.getInstantiater(id).instantiate(this, id, ctx))
      id = readInt
      while (id != 0) {
        registry.getDeserializer(id).asInstanceOf[Deserializer[T]].deserialize(result, this, id, ctx)
        id = readInt
      }
      result
    }
  }
  
}

class ObjectOutputChannel(channel: WritableByteChannel, registry: ClassRegistry)
extends ObjectOutput {

  def writeBytes(buffer: ByteBuffer) {
    channel.write(buffer)
  }
  
  private val buffer: ByteBuffer = ByteBuffer.allocate(8)
  @throws(classOf[java.io.IOException])
  private def writeBytes() {
    buffer.flip()
    writeBytes(buffer)
    buffer.clear()
  }  
  @throws(classOf[java.io.IOException])
  def writeBoolean(value: Boolean) {
    buffer.put(if (value) 1: Byte else 0: Byte)
    writeBytes
  }
  @throws(classOf[java.io.IOException])
  def writeChar(value: Char) {
    buffer.putChar(value)
    writeBytes
  }
  @throws(classOf[java.io.IOException])
  def writeInt(value: Int) {
    buffer.putInt(value)
    writeBytes
  }
  @throws(classOf[java.io.IOException])
  def writeLong(value: Long) {
    buffer.putLong(value)
    writeBytes
  }
  @throws(classOf[java.io.IOException])
  def writeDouble(value: Double) {
    buffer.putDouble(value)
    writeBytes
  }
  
  private val backref = new IdentityHashMap[Any, Int]()
  @throws(classOf[java.io.IOException])
  def write(value: AnyRef) {
    val instSer = registry.getInstanceSerializer(value.getClass())
    writeInt(instSer._1)
    instSer._2.asInstanceOf[InstanceSerializer[AnyRef]].serializeInstance(value, this)
    for (ser <- registry.getSerializers(value.getClass())) {
      writeInt(ser._1)
      ser._2.asInstanceOf[Serializer[AnyRef]].serialize(value, this)
    }
    writeInt(0)
  }
  
}
